<template>
  <div :style="[ctxStyle]"
       @click.stop="clickDocumentHandler"
       @pointerdown.stop
       @contextmenu.prevent
       class="right-menu"
       :id="ctxDomId"
       ref="ctxContainer"
  >
    <div ref="ctxNavBar" class="bg-color2" v-if="!isCloseNavBar">
      <ul class="navBar">
        <slot name="navBar"/>
      </ul>
    </div>

    <div class="bodyBar">
      <slot name="bodyBar"/> 
    </div>

    <div ref="ctxMenu" class="menu bg-color2 text-14">
      <slot name="menu"/>
    </div>
  </div>
</template>

<script>
  import { eventBus } from "../../eventbus/eventbus";

  export default {
    name: "context-menu",
    data() {
      return {
        triggerShowFn: () => {},
        triggerHideFn: () => {},
        ctxStyle: {},
        ctxContainerInfo: {},
        isCloseNavBar: false,// 是否关闭navBar菜单
      };
    },
    props: {
      // 鼠标事件
      mouseEvent: {
        type: Object,
        default: null
      },
      // 当前显示的右键菜单的ID
      selectedRightMenuId: {
        type: String,
        default: ''
      },
      // 每一个右键菜单唯一ID
      rightMenuId: {
        type: String,
        default: ''
      }
    },
    mounted() {
      this.cbId = eventBus.on('closeContextMenu', this.clickDocumentHandler);
      this.closeNavBarId = eventBus.on('closeNavBar', () => {
        this.isCloseNavBar = true;
      });
    },
    watch: {
      mouseEvent(e) {
        if (!e) {
          return;
        }

        if (this.$root.isMobile || e.button === 2) { // 移动端或右键
          this.contextMenuHandler(e);
        } else if (e.button === 0) { // 非移动端且左键
          this.leftKeyHandler(e);
        }
      }
    },
    computed: {
      ctxDomId() {
        return `rightMenu-${this.rightMenuId}`;
      },
    },
    methods: {
      // 绑定隐藏菜单事件
      bindHideEvents() {
        this.triggerHideFn = this.clickDocumentHandler.bind(this);
        document.addEventListener("pointerdown", this.triggerHideFn);
        document.addEventListener("mousewheel", this.triggerHideFn);
      },

      // 取消绑定隐藏菜单事件
      unbindHideEvents() {
        document.removeEventListener("pointerdown", this.triggerHideFn);
        document.removeEventListener("mousewheel", this.triggerHideFn);
        document.removeEventListener("pointermove", this.calculateOpacity);
      },

      // 鼠标按压事件处理器
      clickDocumentHandler() {
        let currentRightMenu = this.$refs.ctxContainer;
        if (currentRightMenu) {
          currentRightMenu.style.zIndex = -1;
          currentRightMenu.style.visibility = 'hidden';
          this.unbindHideEvents();
          eventBus.emit("contextMenuHidden")
          this.isCloseNavBar = false;
        }
      },

      // 右键事件事件处理
      contextMenuHandler(e) {
        e.preventDefault();
        if (e && this.rightMenuId === this.selectedRightMenuId) {
          let body = document.body, x = 0, y = 0;
          let ctxContainer = this.$refs.ctxContainer;
          if (ctxContainer instanceof Node) {
            if (body.append) {
              body.append(ctxContainer);
            } else {
              body.appendChild(ctxContainer);
            }
          }
          ctxContainer.style.zIndex = 9804;
          ctxContainer.style.visibility = 'visible';
          this.bindHideEvents();

          // DOM更新完毕
          this.$nextTick(() => {
            let offset = 5;
            let gap = 30;
            let ctxNavBar = this.$refs.ctxNavBar, ctxMenu = this.$refs.ctxMenu;
            if (ctxNavBar.clientHeight > 0) {
              ctxNavBar.style.marginBottom = 2 * offset + 'px';
            }

            if (body.clientWidth - e.pageX - offset < ctxContainer.clientWidth) {
              x = e.pageX - ctxContainer.clientWidth;
            } else {
              x = e.pageX + offset;
            }
            
            if (ctxMenu.clientHeight + gap + e.pageY < body.clientHeight) {
              y = e.pageY - ctxContainer.clientHeight + ctxMenu.clientHeight;
            } else if (ctxContainer.clientHeight + gap < e.pageY) {
              y = e.pageY - ctxContainer.clientHeight;
            } else {
              y = gap;
            }
            this.layout(x, y);
          })
        }
      },

      // 左键事件事件处理
      leftKeyHandler(e) {
        e.preventDefault();
        if (e && this.rightMenuId === this.selectedRightMenuId) {
          let body = document.querySelector('body'), x = 0, y = 0;
          let ctxContainer = this.$refs.ctxContainer;
          if (ctxContainer instanceof Node) {
            if (body.append) {
              body.append(ctxContainer);
            } else {
              body.appendChild(ctxContainer);
            }
          }
          ctxContainer.style.zIndex = 9400;
          ctxContainer.style.visibility = 'visible';
          this.bindHideEvents();
          // DOM更新完毕
          this.$nextTick(() => {
            let offsetX = 50;
            let offsetY = 80;

            if (body.clientWidth - e.pageX - offsetX < ctxContainer.clientWidth) {
              x = e.pageX - ctxContainer.clientWidth - offsetX;
            } else {
              x = e.pageX + offsetX;
            }
            y = e.pageY - offsetY;
            this.ctxContainerInfo = {
              position: [x, y],
              width: ctxContainer.clientWidth,
              height: ctxContainer.clientHeight,
            };
            this.layout(x, y);
            this.calculateOpacity(e);
            document.addEventListener("pointermove", this.calculateOpacity);
          })
        }
      },

      // 布局
      layout(x, y) {
        this.ctxStyle = {
          left: x + "px",
          top: y + "px",
          opacity: 1
        }
      },

      calculateOpacity(e) {
        let radius = Math.max(this.ctxContainerInfo.width, this.ctxContainerInfo.height) + 100;
        let centerX = this.ctxContainerInfo.position[0] + this.ctxContainerInfo.width / 2;
        let centerY = this.ctxContainerInfo.position[1] - this.ctxContainerInfo.height / 2;
        if (e.pageX <= (centerX - radius) || e.pageX >= (centerX + radius) || e.pageY <= (centerY - radius) || e.pageY >= (centerY + radius)) {
          this.ctxStyle.opacity = 0;
          this.clickDocumentHandler();
        }
        let absX = Math.abs(e.pageX - centerX);
        let absY = Math.abs(e.pageY - centerY);
        let distance = Math.sqrt(absX * absX + absY * absY);
        this.ctxStyle.opacity = 1 - distance / radius;
      }
    },
    beforeDestroy() {
      this.clickDocumentHandler();
      eventBus.off('closeContextMenu', this.cbId);
      eventBus.off('closeNavBar', this.closeNavBarId);
    }
  };
</script>

<style scoped>
  .right-menu {
    position: fixed;
    border-radius: 3px;
    max-width: 170px;
    min-width: 112px;
    visibility: hidden;
    z-index: -1;
    touch-action: none;
  }

  .right-menu:hover {
    opacity: 1!important;
  }

  .right-menu .navBar {
    font-size: 0;
    max-width: 140px;
    border-radius: 2px;
    list-style: none;
    box-shadow: 0 0.5em 1em 0 rgba(0, 0, 0, 0.1);
    -webkit-box-shadow: 0 0.5em 1em 0 rgba(0, 0, 0, 0.1);
    -moz-box-shadow: 0 0.5em 1em 0 rgba(0, 0, 0, 0.1);
  }

  .right-menu .navBar img {
    width: 20px;
  }

  .right-menu .navBar .hasSelect {
    display: flex;
    align-items: center;
  }

  .right-menu .navBar .hasSelect .dropdown .fa-angle-down {
    height: 100%;
    padding: 0 4px;
    cursor: pointer;
  }

  .right-menu .menu {
    border-radius: 2px;
    box-shadow: 0 0.5em 1em 0 rgba(0, 0, 0, 0.1);
    -webkit-box-shadow: 0 0.5em 1em 0 rgba(0, 0, 0, 0.1);
    -moz-box-shadow: 0 0.5em 1em 0 rgba(0, 0, 0, 0.1);
  }

  .menu {
    position: relative;
  }

  .right-menu .menu span {
    min-width: 60px;
    text-align: left;
    display: block;
    color: #1a1a1a;
    cursor: default;
    padding: 2px 7px;
    text-overflow: ellipsis;
    white-space: nowrap;
    overflow: hidden;
    transition: .2s linear;
  }

  .right-menu .menu span:not(.ui-menu-divider):hover {
    background-color: var(--bg-color9);
  }

  .bodyBar {
    margin-top: 10px!important;
  }

  .right-menu .menu .ui-menu-divider:first-child,
  .right-menu .menu .ui-menu-divider:last-child {
    display: none;
  }
</style>
